package net.lcqf.tcp.game.match.runnable;

import net.lcqf.tcp.constants.Constants;
import net.lcqf.tcp.game.match.MatchBaseService;
import net.lcqf.tcp.game.match.MatchFactory;
import net.lcqf.tcp.game.match.TeamType;
import net.lcqf.tcp.game.match.model.MatchPlayer;
import net.lcqf.tcp.game.match.model.Team;
import net.lcqf.tcp.send.message.Match;
import net.lcqf.tcp.utils.LocalCacheUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.*;

/**
 *
 * 匹配算法后期根据策划提出的需求整改
 * 1、按隐藏分分层
 * 2、优先匹配等待时间长的玩家
 * 3、动态计算允许的ELO差值‌
 * +----------------------------------------------------------------------
 * // | Talk To [ 聊来改善地区金融服务环境，助力企业发展 ]
 * // +----------------------------------------------------------------------
 * // | Copyright (c) 2021-2025 聊来 All rights reserved.
 * // +----------------------------------------------------------------------
 * // | Licensed ( <a href="http://www.apache.org/licenses/LICENSE-2.0">apache</a> )
 * // +----------------------------------------------------------------------
 * // | @Author: 聊来 <18970881148@qq.com>
 * // +----------------------------------------------------------------------
 * // | DateTime: 2025/3/7 22:52
 * // +----------------------------------------------------------------------
 */
@Service
public class Matcher5v5Runnable implements Runnable, MatchBaseService {

    private static final Logger logger = LoggerFactory.getLogger(Matcher5v5Runnable.class);

    private static final int BASE_ELO_DIFF = 100;
    private static final int MAX_WAIT_SECONDS = 15;

    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2, r -> new Thread(r, "team-5v5-" + LocalCacheUtil.MATCH_EXECUTOR.getAndIncrement()));

    private final ConcurrentLinkedQueue<MatchPlayer> matchPlayerQueue = new ConcurrentLinkedQueue<>();

    private final Match match;

    public Matcher5v5Runnable(Match match) {
        this.match = match;
    }

    @PostConstruct
    public void start() {
        scheduler.scheduleAtFixedRate(this, 0, 500, TimeUnit.MILLISECONDS);
        MatchFactory.register(TeamType.FIVE, this);
    }


    @Override
    public void run() {
        List<MatchPlayer> candidates = new ArrayList<>(matchPlayerQueue.size());

        // 取出当前队列所有玩家
        while (!matchPlayerQueue.isEmpty()) {
            candidates.add(matchPlayerQueue.poll());
        }


        for (int i = candidates.size() - 1; i >= 0; i--) {
            if (!candidates.get(i).getChannel().isActive()) {
                candidates.remove(i);
            }
        }

        // 按加入时间，优先处理加入时间最早的玩家‌
        candidates.sort(Comparator.comparingLong(MatchPlayer::getJoinTime));

        int index = 0;
        while (index + 2 * Constants.NUM_FIVE <= candidates.size()) {
            List<MatchPlayer> teamA = new ArrayList<>(Constants.NUM_FIVE);
            List<MatchPlayer> teamB = new ArrayList<>(Constants.NUM_FIVE);

            // 选取相邻ELO范围的玩家‌
            for (int i = 0; i < Constants.NUM_FIVE; i++) {
                MatchPlayer p = candidates.get(index + i);
                if (isValidMatch(p, candidates.get(index + Constants.NUM_FIVE + i))) {
                    teamA.add(p);
                    teamB.add(candidates.get(index + Constants.NUM_FIVE + i));
                } else {
                    // 匹配失败则回退
                    matchPlayerQueue.addAll(candidates.subList(index, candidates.size()));
                    return;
                }
            }

            // 触发匹配成功事件
            match.onMatchSuccess(new Team(teamA), new Team(teamB), "5v5");
            index += 2 * Constants.NUM_FIVE;
        }

        // 未匹配的玩家放回队列
        matchPlayerQueue.addAll(candidates.subList(index, candidates.size()));
    }


    /**
     * 将玩家添加到队列
     * @param player 玩家信息
     * @return {@code boolean}
     */
    @Override
    public boolean addPlayer(MatchPlayer player) {
        return matchPlayerQueue.offer(player);
    }



    /**
     * 将玩家移除队列
     * @param channelId 通信的唯一标识
     * @return {@code boolean}
     */
    @Override
    public boolean removePlayer(String channelId) {
        return matchPlayerQueue.removeIf(match -> match.getChannelId().equals(channelId));
    }



    /**
     * 判断是否有效匹配‌
     * @param p1 玩家1
     * @param p2 玩家2
     * @return {@code boolean}
     */
    private boolean isValidMatch(MatchPlayer p1, MatchPlayer p2) {
        long waitTime = (System.currentTimeMillis() - p1.getJoinTime()) / 1000;
        int eloDiff = Math.abs(p1.getElo() - p2.getElo());

        // 等待越久，允许的ELO差异越大
        int maxAllowedDiff = waitTime > MAX_WAIT_SECONDS ? 2 * BASE_ELO_DIFF : BASE_ELO_DIFF;
        return eloDiff <= maxAllowedDiff;
    }

    /**
     * 鬼知道能不能用上
     */
    public void shutdown() {
        scheduler.shutdown();
    }
}
